﻿using System;
using System.IO;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Utils
{
    public class SoundUtils
    {
        private const String NORMALBACKGROUNDMUSICFILE = "background.wav";
        private const String FOOTSTEPFILE = "footstep.wav";
        private const String TESTPOSFILE = "bugs_29.wav";
        private const int NUMOFSOUNDS = 3;
        private const int Foot_Step = 2;
        private const int Pos_Test = 1;
        private const int Normal_Background = 0;
        private static int[] buffers;
        private static int[] sources;
        private const int NUMOFSTATICSOURCES = 3;
        private const int CENTER = 1;
        private const int BACKGROUND = 0;
        private const int CAMERA = 2;
      
        // Loads a wave/riff audio file.
        public static byte[] LoadWave(Stream stream, out int channels, out int bits, out int rate)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");

            using (BinaryReader reader = new BinaryReader(stream))
            {
                // RIFF header
                string signature = new string(reader.ReadChars(4));
                if (signature != "RIFF")
                    throw new NotSupportedException("Specified stream is not a wave file.");

                int riff_chunck_size = reader.ReadInt32();

                string format = new string(reader.ReadChars(4));
                if (format != "WAVE")
                    throw new NotSupportedException("Specified stream is not a wave file.");

                // WAVE header
                string format_signature = new string(reader.ReadChars(4));
                if (format_signature != "fmt ")
                    throw new NotSupportedException("Specified wave file is not supported.");

                int format_chunk_size = reader.ReadInt32();
                int audio_format = reader.ReadInt16();
                int num_channels = reader.ReadInt16();
                int sample_rate = reader.ReadInt32();
                int byte_rate = reader.ReadInt32();
                int block_align = reader.ReadInt16();
                int bits_per_sample = reader.ReadInt16();

                string data_signature = new string(reader.ReadChars(4));
                if (data_signature != "data")
                    throw new NotSupportedException("Specified wave file is not supported.");

                int data_chunk_size = reader.ReadInt32();

                channels = num_channels;
                bits = bits_per_sample;
                rate = sample_rate;

                return reader.ReadBytes((int)reader.BaseStream.Length);
            }
        }

        public static ALFormat GetSoundFormat(int channels, int bits)
        {
            switch (channels)
            {
                case 1: return bits == 8 ? ALFormat.Mono8 : ALFormat.Mono16;
                case 2: return bits == 8 ? ALFormat.Stereo8 : ALFormat.Stereo16;
                default: throw new NotSupportedException("The specified sound format is not supported.");
            }
        }

        public static void setListenerLocation(Point3D location, Angle3D Azimuth, Angle3D Angle, float deltax, float deltay, float deltaz)
        {
            OpenTK.Vector3 lookAt = new OpenTK.Vector3();
            lookAt.Z -= (float)Angle.SinAngle * (float)Azimuth.SinAngle;
            lookAt.X += (float)(Angle.CosAngle * Azimuth.SinAngle);
            lookAt.Y += (float) Azimuth.CosAngle;
            OpenTK.Vector3 unity = new OpenTK.Vector3(0, 1, 0);
            AL.Source(sources[CAMERA], ALSource3f.Position, (float)location.X, (float)location.Y, (float)location.Z);
            AL.Source(sources[CAMERA], ALSource3f.Velocity, -deltax * 30, -deltay * 30, -deltaz * 30);
            AL.Listener(ALListener3f.Position, (float)location.X, (float)location.Y, (float)location.Z);
            AL.Listener(ALListenerf.Gain, 1.0f);
            AL.Listener(ALListenerfv.Orientation,ref lookAt,ref unity);
            AL.Listener(ALListener3f.Velocity, -deltax * 30, -deltay * 30, -deltaz * 30);

        }
        public static void Intialize()
        {
            
            buffers = AL.GenBuffers(NUMOFSOUNDS);
            
            sources = AL.GenSources(NUMOFSTATICSOURCES);

            AL.DopplerFactor(2);
            AL.SpeedOfSound(200);
            
            int channels, bits_per_sample, sample_rate;
            byte[] sound_data = LoadWave(File.Open(Directory.GetCurrentDirectory() + "\\..\\..\\..\\Model\\Sounds\\" + NORMALBACKGROUNDMUSICFILE, FileMode.Open), out channels, out bits_per_sample, out sample_rate);
            AL.BufferData(buffers[Normal_Background], GetSoundFormat(channels, bits_per_sample), sound_data, sound_data.Length, sample_rate);
            byte[] sound_data2 = LoadWave(File.Open(Directory.GetCurrentDirectory() + "\\..\\..\\..\\Model\\Sounds\\" + TESTPOSFILE, FileMode.Open), out channels, out bits_per_sample, out sample_rate);
            AL.BufferData(buffers[Pos_Test], GetSoundFormat(channels, bits_per_sample), sound_data2, sound_data2.Length, sample_rate);
            byte[] sound_data3 = LoadWave(File.Open(Directory.GetCurrentDirectory() + "\\..\\..\\..\\Model\\Sounds\\" + FOOTSTEPFILE, FileMode.Open), out channels, out bits_per_sample, out sample_rate);
            AL.BufferData(buffers[Foot_Step], GetSoundFormat(channels, bits_per_sample), sound_data3, sound_data3.Length, sample_rate);
     
            AL.Source(sources[BACKGROUND], ALSourceb.Looping, true);
            AL.Source(sources[CENTER], ALSource3f.Velocity, 1, 0, 0);
            AL.Source(sources[BACKGROUND], ALSourcef.Gain, .3f);
            AL.Source(sources[BACKGROUND], ALSourcef.Pitch, 1.0f);

            AL.Source(sources[CENTER], ALSource3f.Position,0,0,0);
            AL.Source(sources[CENTER], ALSource3f.Velocity, 0, 0, 0);
            AL.Source(sources[CENTER], ALSourceb.Looping, false);
            AL.Source(sources[CENTER], ALSourcef.Gain, 1.0f);
            AL.Source(sources[CENTER], ALSourcef.Pitch, 1.0f);

            AL.Source(sources[CAMERA], ALSource3f.Position, 0, 0, 0);
            AL.Source(sources[CAMERA], ALSource3f.Velocity, 0, 0, 0);
            AL.Source(sources[CAMERA], ALSourceb.Looping, false);
            AL.Source(sources[CAMERA], ALSourcef.Gain, 1.0f);
            AL.Source(sources[CAMERA], ALSourcef.Pitch, 1.0f);

            PlayNormalBackgroundMusic();

        }

        public static void PlayNormalBackgroundMusic()
        {
            int buffer;
            AL.GetSource(sources[BACKGROUND], ALGetSourcei.Buffer, out buffer);
            if (buffer == buffers[Normal_Background]) return;
            AL.SourceStop(sources[BACKGROUND]);
            //Console.Out.WriteLine("playing song");
            AL.Source(sources[BACKGROUND], ALSourcei.Buffer, buffers[Normal_Background]);
           /* OpenTK.Audio.OpenAL.ALSourceState ssv; int outVal;
            OpenTK.Audio.OpenAL.AL.GetSource(sources[BACKGROUND], OpenTK.Audio.OpenAL.ALGetSourcei.SourceState, out outVal);
              ssv = (OpenTK.Audio.OpenAL.ALSourceState)outVal;*/
              
            AL.SourcePlay(sources[BACKGROUND]);
          
           /* OpenTK.Audio.OpenAL.AL.GetSource(sources[BACKGROUND], OpenTK.Audio.OpenAL.ALGetSourcei.SourceState, out outVal);
            ssv = (OpenTK.Audio.OpenAL.ALSourceState)outVal;
            switch (ssv)
            {
                case OpenTK.Audio.OpenAL.ALSourceState.Initial: Console.Write("SourceStatus: Inital"); break;
                case OpenTK.Audio.OpenAL.ALSourceState.Paused: Console.Write("SourceStatus: Paused"); break;
                case OpenTK.Audio.OpenAL.ALSourceState.Playing: Console.Write("SourceStatus: Playing"); break;
                case OpenTK.Audio.OpenAL.ALSourceState.Stopped: Console.Write("SourceStatus: Stopped"); break;
                default: Console.Write("SourceStatus: Error"); break;
            }*/
        }
        public static void PlayFootStep()
        {
            int buffer;
            AL.GetSource(sources[CAMERA], ALGetSourcei.Buffer, out buffer);
            OpenTK.Audio.OpenAL.ALSourceState ssv; int outVal;
            OpenTK.Audio.OpenAL.AL.GetSource(sources[CAMERA], OpenTK.Audio.OpenAL.ALGetSourcei.SourceState, out outVal);
            ssv = (OpenTK.Audio.OpenAL.ALSourceState)outVal;

            if (buffer == buffers[Foot_Step] && ssv == ALSourceState.Playing) return;
            AL.SourceStop(sources[CAMERA]);
            //Console.Out.WriteLine("playing song");
            AL.Source(sources[CAMERA], ALSourcei.Buffer, buffers[Foot_Step]);
            AL.SourcePlay(sources[CAMERA]);
            //Console.WriteLine("play footstep");
            
        }

        public static void PlayTestPostion()
        {
            int buffer;
            AL.GetSource(sources[CENTER], ALGetSourcei.Buffer, out buffer);
            OpenTK.Audio.OpenAL.ALSourceState ssv; int outVal;
            OpenTK.Audio.OpenAL.AL.GetSource(sources[CENTER], OpenTK.Audio.OpenAL.ALGetSourcei.SourceState, out outVal);
              ssv = (OpenTK.Audio.OpenAL.ALSourceState)outVal;

            if (buffer == buffers[Pos_Test] && ssv == ALSourceState.Playing) return;
            AL.SourceStop(sources[CENTER]);
            //Console.Out.WriteLine("playing song");
            AL.Source(sources[CENTER], ALSourcei.Buffer, buffers[Pos_Test]);
            AL.SourcePlay(sources[CENTER]);
            //Console.WriteLine("play bugs");
        }

        public static void getListenerLocation(out float x, out float y, out float z)
        {
            AL.GetListener(ALListener3f.Position, out x, out y, out z);
        }

        public static void setCenterLocation(Point3D point3D, Point3D offset)
        {
            AL.Source(sources[CENTER], ALSource3f.Position, (float)point3D.X, (float)point3D.Y, (float)point3D.Z);
            AL.Source(sources[CENTER], ALSource3f.Velocity, 30 * (float)offset.X, 30 * (float)offset.Y, 30 * (float)offset.Z);
        }

    }
}
